﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;

namespace CubePuzzle
{
    struct vertexFormatPosTex
    {
        public Vector3 Pos;
        public Vector2 TexCoord;

        public vertexFormatPosTex(Vector3 _Pos, Vector2 _TexCoord)
        {
            Pos = _Pos;
            TexCoord = _TexCoord;
        }

        public static readonly VertexFormats FVF = VertexFormats.Position | VertexFormats.Normal | VertexFormats.Texture0;
    }

    struct vertexFormatPosCol
    {
        public Vector3 Pos;
        public int Color;

        public vertexFormatPosCol(Vector3 _Pos, int _Color)
        {
            Pos = _Pos;
            Color = _Color;
        }

        public static readonly VertexFormats FVF = VertexFormats.Position | VertexFormats.Normal | VertexFormats.Texture0;
    }

    struct vertexFormatPosNormTex
    {
        public Vector3 Pos;
        public Vector3 Norm;
        public Vector2 Tex;

        public vertexFormatPosNormTex(Vector3 _Pos, Vector3 _Norm, Vector2 _Tex)
        {
            Pos = _Pos;
            Norm = _Norm;
            Tex = _Tex;
        }

        public static readonly VertexFormats FVF = VertexFormats.Position | VertexFormats.Normal | VertexFormats.Texture0;
    }
 
    abstract public class DxApp
    {
        public Microsoft.DirectX.Direct3D.Device device;
        public VertexBuffer vb;
        public VertexDeclaration vd;
        public Microsoft.DirectX.DirectInput.Device keyb;
        public Microsoft.DirectX.DirectInput.Device mouse;

        public Texture [] cubeTextures;

        public Vector3 cameraPosition;
        public Vector3 cameraAt;
        public Vector3 cameraForward;
        public Vector3 cameraUpVector;

        public float cameraDistance;
        public float u = -(float)Math.PI / 2;
        public float v = (float)Math.PI / 2;

        public MouseState currentMouseState;

        public Microsoft.DirectX.Direct3D.Effect effect;

        public Matrix matView;
        public Matrix matProjection;
        public Matrix matWorld;

        public Game form;

        public Boolean deviceOK = false;

        public void InitializeKeyboard()
        {
            keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
            keyb.SetCooperativeLevel(form, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            keyb.Acquire();
        }

        public void InitializeMouse()
        {
            mouse = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Mouse);
            mouse.SetCooperativeLevel(form, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            mouse.Acquire();
        }

        public bool InitializeDevice(IntPtr handle)
        {
            try
            {
                PresentParameters presentParams = new PresentParameters();
                presentParams.Windowed = true;
                presentParams.SwapEffect = SwapEffect.Discard;
                presentParams.AutoDepthStencilFormat = DepthFormat.D16;
                presentParams.EnableAutoDepthStencil = true;

                Caps DevCaps = D3D.Manager.GetDeviceCaps(0, D3D.DeviceType.Hardware);
                D3D.DeviceType DevType = D3D.DeviceType.Reference;
                CreateFlags DevFlags = CreateFlags.SoftwareVertexProcessing;
                if ((DevCaps.VertexShaderVersion >= new Version(2, 0)) && (DevCaps.PixelShaderVersion >= new Version(2, 0)))
                {
                    DevType = D3D.DeviceType.Hardware;
                    if (DevCaps.DeviceCaps.SupportsHardwareTransformAndLight)
                    {
                        DevFlags = CreateFlags.HardwareVertexProcessing;
                        if (DevCaps.DeviceCaps.SupportsPureDevice)
                        {
                            DevFlags |= CreateFlags.PureDevice;
                        }
                    }
                }

                device = new D3D.Device(0, DevType, handle, DevFlags, presentParams);

                device.DeviceReset += new EventHandler(this.HandleDeviceReset);
                SetUpCamera();
                deviceOK = true;
                return true;
            }
            catch (DirectXException)
            {
                return false;
            }
        }

        abstract public void ReadMouse();
        abstract public void ReadKeyBoard();
        abstract public void Render();
        abstract public void UpdateCamera();
        abstract public void HandleDeviceReset(object sender, EventArgs e);
        abstract public void SetUpCamera();
        abstract public void InitTextures();

        public void AllocateResources()
        {
            vb = new VertexBuffer(typeof(vertexFormatPosNormTex), 24, device, Usage.WriteOnly, vertexFormatPosNormTex.FVF, Pool.Managed);
            effect = D3D.Effect.FromFile(device, @"HLSL.fx", null, ShaderFlags.None, null);
            device.RenderState.CullMode = Cull.None;
        }

        public void FillResources()
        {
            vertexFormatPosNormTex[] vertices = new vertexFormatPosNormTex[24];

            float x = 1.0f; float y = 1.0f; float z = 1.0f;

            vertices[0] = new vertexFormatPosNormTex(new Vector3(x, y, z), new Vector3(0, 0, 1), new Vector2(0, 1));
            vertices[1] = new vertexFormatPosNormTex(new Vector3(x, 0, z), new Vector3(0, 0, 1), new Vector2(1, 0));
            vertices[2] = new vertexFormatPosNormTex(new Vector3(0, 0, z), new Vector3(0, 0, 1), new Vector2(1, 1));
            vertices[3] = new vertexFormatPosNormTex(new Vector3(0, y, z), new Vector3(0, 0, 1), new Vector2(0, 0));
            vertices[4] = new vertexFormatPosNormTex(new Vector3(x, y, 0), new Vector3(0, 0, -1), new Vector2(0, 1));
            vertices[5] = new vertexFormatPosNormTex(new Vector3(0, y, 0), new Vector3(0, 0, -1), new Vector2(1, 0));
            vertices[6] = new vertexFormatPosNormTex(new Vector3(0, 0, 0), new Vector3(0, 0, -1), new Vector2(1, 1));
            vertices[7] = new vertexFormatPosNormTex(new Vector3(x, 0, 0), new Vector3(0, 0, -1), new Vector2(0, 0));
            vertices[8] = new vertexFormatPosNormTex(new Vector3(x, y, 0), new Vector3(1, 0, 0), new Vector2(1, 0));
            vertices[9] = new vertexFormatPosNormTex(new Vector3(x, 0, 0), new Vector3(1, 0, 0), new Vector2(0, 1));
            vertices[10] = new vertexFormatPosNormTex(new Vector3(x, 0, z), new Vector3(1, 0, 0), new Vector2(0, 0));
            vertices[11] = new vertexFormatPosNormTex(new Vector3(x, y, z), new Vector3(1, 0, 0), new Vector2(1, 1));
            vertices[12] = new vertexFormatPosNormTex(new Vector3(0, y, z), new Vector3(-1, 0, 0), new Vector2(0, 1));
            vertices[13] = new vertexFormatPosNormTex(new Vector3(0, 0, z), new Vector3(-1, 0, 0), new Vector2(1, 0));
            vertices[14] = new vertexFormatPosNormTex(new Vector3(0, 0, 0), new Vector3(-1, 0, 0), new Vector2(1, 1));
            vertices[15] = new vertexFormatPosNormTex(new Vector3(0, y, 0), new Vector3(-1, 0, 0), new Vector2(0, 0));
            vertices[16] = new vertexFormatPosNormTex(new Vector3(x, y, 0), new Vector3(0, 1, 0), new Vector2(0, 1));
            vertices[17] = new vertexFormatPosNormTex(new Vector3(x, y, z), new Vector3(0, 1, 0), new Vector2(1, 0));
            vertices[18] = new vertexFormatPosNormTex(new Vector3(0, y, z), new Vector3(0, 1, 0), new Vector2(1, 1));
            vertices[19] = new vertexFormatPosNormTex(new Vector3(0, y, 0), new Vector3(0, 1, 0), new Vector2(0, 0));
            vertices[20] = new vertexFormatPosNormTex(new Vector3(x, 0, z), new Vector3(0, -1, 0), new Vector2(1, 0));
            vertices[21] = new vertexFormatPosNormTex(new Vector3(x, 0, 0), new Vector3(0, -1, 0), new Vector2(0, 1));
            vertices[22] = new vertexFormatPosNormTex(new Vector3(0, 0, 0), new Vector3(0, -1, 0), new Vector2(1, 1));
            vertices[23] = new vertexFormatPosNormTex(new Vector3(0, 0, z), new Vector3(0, -1, 0), new Vector2(0, 0));


            vb.SetData(vertices, 0, LockFlags.None);

            //Unlock the vb before you can use it elsewhere
            vb.Unlock();

            VertexElement[] velements = new VertexElement[]
            {
                new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
                new VertexElement(0, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 0),
                new VertexElement(0, 24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
                VertexElement.VertexDeclarationEnd
            };
            vd = new VertexDeclaration(device, velements);
        }        
    }
}
